package model

import (
	"errors"
	"gitee.com/sdumeeting/sfu-server/global"
	"gitee.com/sdumeeting/sfu-server/pkg/util"
	"github.com/chuckpreslar/emission"
	"github.com/gorilla/websocket"
	"sync"
	"time"
)

type Connection struct {
	// 结构体内嵌，继承 Emitter 的变量和方法，用于事件派发
	emission.Emitter

	// 代表 WebSocket 连接
	socket *websocket.Conn

	// 互斥锁，在发送消息或关闭连接时会加锁
	// 解决 socket 和 closed 的竞争问题
	mutex *sync.Mutex

	// 用于标识该连接是否已经关闭
	// 初始值是 false，在 socket 关闭后，会将该变量的值置为 true
	closed bool
}

// NewConnection 新建连接
func NewConnection(socket *websocket.Conn) *Connection {
	// 将结构体中的变量进行初始化
	conn := &Connection{
		Emitter: *emission.NewEmitter(),
		socket:  socket,
		mutex:   new(sync.Mutex),
		closed:  false,
	}

	// SetCloseHandler 为从 peer 接收的关闭消息设置处理程序
	conn.socket.SetCloseHandler(func(_ int, _ string) error {
		conn.Emit("close")
		conn.closed = true
		return nil
	})

	return conn
}

// Receive 接收消息
func (conn *Connection) Receive() {
	// 用于接收消息的通道
	in := make(chan []byte)

	// 用于控制协程停止的通道
	stop := make(chan struct{})

	// 该 ticker 包含一个通道字段，每个一段时间 d 就向该通道发送当前的时间
	// 在这里用来做心跳检测
	pingTicker := time.NewTicker(5 * time.Second)

	go func() {
		// 不断地读取消息
		for {
			_, message, err := conn.socket.ReadMessage()
			if err != nil {
				if !conn.closed {
					conn.Emit("close")
				}

				close(stop)
				break
			}
			in <- message
		}
	}()

	for {
		select {
		case _ = <-pingTicker.C:
			// 创建一个空的心跳包
			heartPackage := map[string]interface{}{
				"type": "heartPackage",
				"data": "",
			}

			// 将心跳包发送出去，并进行相应的错误处理
			if err := conn.Send(util.Marshal(heartPackage)); err != nil {
				global.Logger.Error("conn.Send err: %v", err)
				pingTicker.Stop()
				return
			}
		case message := <-in:
			// 将读取到的消息解析并处理
			conn.Emit("message", message)
		case <-stop:
			return
		}
	}
}

// Send 发送消息
func (conn *Connection) Send(message string) error {
	conn.mutex.Lock()
	defer conn.mutex.Unlock()

	// 判断连接是否关闭
	if conn.closed {
		return errors.New("websocket: connection closed")
	}

	return conn.socket.WriteMessage(websocket.TextMessage, []byte(message))
}

// Close 关闭连接
func (conn *Connection) Close() {
	conn.mutex.Lock()
	defer conn.mutex.Unlock()

	if !conn.closed {
		err := conn.socket.Close()
		if err != nil {
			panic(err.Error())
		}
		conn.closed = true
	} else {
		global.Logger.Warnf("conn %v has already closed", conn)
	}
}
